
/**
 * CRC32计算 (兼容Java/TypeScript/Python)
 * @author 杨永良
 * @date 22年08月09日 20:11
 * 
 * 		let testCode: string = "This is a Test!!";
		let crc:number = CompatibleCRC32.get32BitDec(testCode);
		console.log("CRC:" + crc.toString());
		console.log("CRC:" + Math.abs(crc).toString(16));

		testCode = "这是个测试Hash的例子";
		crc = CompatibleCRC32.get32BitDec(testCode);
		console.log("CRC:" + crc.toString());
		testCode = "This is a 例子!!";
		crc = CompatibleCRC32.get32BitDec(testCode);
		console.log("CRC:" + crc.toString());
		let scrc:string = CompatibleCRC32.get32BitHex(testCode);
		console.log(scrc);

**/
export default class CompatibleCRC32 {
    /**
     * CRC32 = X32 + X26 + X23 + X22 + X16 + X12 + X11 + X10
     *  + X8 + X7 + X5 + X4 + X2 + X1 + X0
     */
    private static CN:number = 0x04C11DB7;
    private static HEX_MAP:string[] = ['0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'];
    private static PTI_TABLE:Array<number> = null;

    public static get32BitDec(text:string):number {
        let bytes:Array<number> = this.stringToBytes(text);
        return this.getCrcByBytes(bytes)
    }

    public static getCrcByBytes(datas:Array<number>):number {
        this.buildTable();
        let crc32:number = 0xFFFFFFFF;
        if (null == datas || datas.length < 1) {
            return crc32;
        }
        for (let i=0; i<datas.length; i++) {
            let data:number = datas[i]
            crc32 = (crc32 << 8) ^ CompatibleCRC32.PTI_TABLE[(crc32 >>> 24) ^ (data & 0xFF)];
        }
        return crc32;
    }

    public static get32BitHex(text:string):string{
        let dec:number =  this.get32BitDec(text);
        let sb:string = "";
        sb += this.HEX_MAP[(dec>>>28) & 0xF];
        sb += this.HEX_MAP[(dec>>>24) & 0xF];
        sb += this.HEX_MAP[(dec>>>20) & 0xF];
        sb += this.HEX_MAP[(dec>>>16) & 0xF];
        sb += this.HEX_MAP[(dec>>>12) & 0xF];
        sb += this.HEX_MAP[(dec>>>8) & 0xF];
        sb += this.HEX_MAP[(dec>>>4) & 0xF];
        sb += this.HEX_MAP[dec & 0xF];
        return sb;
    }

	/**	
	 * string 转 byte	
	 * @param str	
	 */
    private static stringToBytes(str) {
		var bytes = new Array();
		var len, c;
		len = str.length;
		for (var i = 0; i < len; i++) {
			c = str.charCodeAt(i);
			if (c >= 0x010000 && c <= 0x10FFFF) {
				bytes.push(((c >> 18) & 0x07) | 0xF0);
				bytes.push(((c >> 12) & 0x3F) | 0x80);
				bytes.push(((c >> 6) & 0x3F) | 0x80);
				bytes.push((c & 0x3F) | 0x80);
			} else if (c >= 0x000800 && c <= 0x00FFFF) {
				bytes.push(((c >> 12) & 0x0F) | 0xE0);
				bytes.push(((c >> 6) & 0x3F) | 0x80);
				bytes.push((c & 0x3F) | 0x80);
			} else if (c >= 0x000080 && c <= 0x0007FF) {
				bytes.push(((c >> 6) & 0x1F) | 0xC0);
				bytes.push((c & 0x3F) | 0x80);
			} else {
				bytes.push(c & 0xFF);
			}
		}
		return bytes;
	}

    private static buildTable() {
        if(this.PTI_TABLE != null){
            return;
        }
        this.PTI_TABLE = new Array(256);
        let nData:number = 0;
        let nAccum:number = 0;
        for (let i = 0; i < 256; i++) {
            nData = i << 24;
            nAccum = 0;
            for (let j = 0; j < 8; j++) {
                if (0 != ((nData ^ nAccum) & 0x80000000)) {
                    nAccum = (nAccum << 1) ^ this.CN;
                } else {
                    nAccum <<= 1;
                }
                nData <<= 1;
            }
            CompatibleCRC32.PTI_TABLE[i] = nAccum;
        }
    }

}